/*
 * sbt
 * Copyright 2011 - 2018, Lightbend, Inc.
 * Copyright 2008 - 2010, Mark Harrah
 * Licensed under Apache License 2.0 (see LICENSE)
 */

package sbt.internal.util

import sbt.util._
import java.io.{ File, PrintWriter }

/**
 * Provides the current global logging configuration.
 *
 * `full` is the current global logger. It should not be set directly because it is generated as
 * needed from `backing.newLogger`. `console` is where all logging from all ConsoleLoggers should
 * go. `backed` is the Logger that other loggers should feed into. `backing` tracks the files that
 * persist the global logging. `newLogger` creates a new global logging configuration from a sink
 * and backing configuration.
 */
final case class GlobalLogging(
    full: ManagedLogger,
    console: ConsoleOut,
    backed: Appender,
    backing: GlobalLogBacking,
    newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging
)

final case class GlobalLogging1(
    full: Logger,
    console: ConsoleOut,
    backed: AbstractLogger,
    backing: GlobalLogBacking,
    newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1
)

/**
 * Tracks the files that persist the global logging. `file` is the current backing file. `last` is
 * the previous backing file, if there is one. `newBackingFile` creates a new temporary location for
 * the next backing file.
 */
final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile: () => File) {

  /** Shifts the current backing file to `last` and sets the current backing to `newFile`. */
  def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newBackingFile)

  /**
   * Shifts the current backing file to `last` and sets the current backing to a new temporary file
   * generated by `newBackingFile`.
   */
  def shiftNew() = shift(newBackingFile())

  /**
   * If there is a previous backing file in `last`, that becomes the current backing file and the
   * previous backing is cleared. Otherwise, no changes are made.
   */
  def unshift = GlobalLogBacking(last getOrElse file, None, newBackingFile)

}

object GlobalLogBacking {
  def apply(newBackingFile: => File): GlobalLogBacking =
    GlobalLogBacking(newBackingFile, None, () => newBackingFile)
}

object GlobalLogging {
  import java.util.concurrent.atomic.AtomicInteger

  private def generateName: String = "GlobalLogging" + generateId.incrementAndGet
  private val generateId: AtomicInteger = new AtomicInteger

  def initial1(
      newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1,
      newBackingFile: => File,
      console: ConsoleOut
  ): GlobalLogging1 = {
    val log = ConsoleLogger(console)
    GlobalLogging1(log, console, log, GlobalLogBacking(newBackingFile), newLogger)
  }

  def initial(
      newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging,
      newBackingFile: => File,
      console: ConsoleOut
  ): GlobalLogging = {
    val loggerName = generateName
    val log = LoggerContext.globalContext.logger(loggerName, None, None)
    val appender = ConsoleAppender(ConsoleAppender.generateName(), console)
    LoggerContext.globalContext.addAppender(loggerName, appender -> Level.Info)
    GlobalLogging(log, console, appender, GlobalLogBacking(newBackingFile), newAppender)
  }
}
